Release 10.1A: OpenEdge Development:
Progress 4GL Handbook


Handling the ERROR condition

Your application can encounter an error condition whenever Progress cannot execute a statement properly, such as trying to find a record that does not exist or create a record with a duplicate value in a unique index. Progress has a built-in error message associated with each such error condition and, by default, displays it (or writes it to an error log on an AppServer).

For example, if your procedure executes a FIND statement for a nonexistent Customer, you get the error shown in Figure 17–10.

Figure 17–10: FIND error message

The error number in parentheses lets you locate the message number under the Help menu to get more information on the error. It can also help you when you are reporting unexpected errors to technical support.

You can also generate the ERROR condition programmatically using the RETURN ERROR statement, either as part of the ON phrase of a block header or as a statement of its own. If your application has associated a keyboard key with the ERROR condition then Progress also raises ERROR when the user presses that key.

ERROR-STATUS system handle

In most cases, you do not want raw error messages to be shown to users, even when it is their mistake that causes the error. The alert box in Figure 17–10, for example, is not a very friendly or informative way to present an error to a user. Even more important, it is essential that your procedures anticipate all possible error conditions whether they are caused by a user action or not, and respond to them, in some cases by suppressing an error message altogether. In addition, your application must define a mechanism for returning errors generated in an AppServer session back to the client, because by default Progress messages simply go to the server log file and are never seen.

To check for errors programmatically, you use the ERROR-STATUS system handle. Many Progress statements support the NO-ERROR option as the last keyword in the statement. If you specify this option, that statement does not generate the ERROR condition. Instead, if the statement cannot execute properly, execution continues with the next statement. You can then check whether an error occurred by examining the attributes of the ERROR-STATUS system handle.

The ERROR-STATUS handle contains information on the last statement executed with the NO-ERROR option. The logical attribute ERROR-STATUS:ERROR tells you whether an error occurred. Because in some cases a single error can return multiple messages, the NUM-MESSAGES attribute tells you how many messages there are. The GET-MESSAGE(<msg-num>) method returns the text of the message, and the GET-NUMBER(msg-num) method returns the internal message number. Here’s a simple example:

DEFINE VARIABLE iMsg AS INTEGER    NO-UNDO. 
FIND Customer WHERE CustNum = 9876 NO-ERROR. 
IF ERROR-STATUS:ERROR THEN 
DO iMsg = 1 TO ERROR-STATUS:NUM-MESSAGES: 
    MESSAGE "Error number: " ERROR-STATUS:GET-NUMBER(iMsg) SKIP 
             ERROR-STATUS:GET-MESSAGE(iMsg) 
        VIEW-AS ALERT-BOX ERROR. 
END. 

Because there is no Customer 9876, you get an error and your code displays the message box shown in Figure 17–11.

Figure 17–11: Example error message

Because you are intercepting the error, you can handle it more gracefully than this and also have your program logic proceed accordingly. You can check the message number using the GET-NUMBER method and put code in your application to deal with each of the possible error conditions.

Remember also that Progress provides special built-in functions, such as AVAILABLE and LOCKED, to make it easier for you to tell when certain common errors have occurred:

FIND Customer WHERE CustNum = 9876 NO-ERROR. 
IF NOT AVAILABLE Customer THEN 
  MESSAGE "So sorry, but this Customer does not seem to be there." 
    VIEW-AS ALERT-BOX INFORMATION. 

Figure 17–12 shows the result.

Figure 17–12: Example information message

Note that the ERROR-STATUS handle holds only error conditions and messages for the most recently executed statement with the NO-ERROR option. It does not accumulate errors over multiple statements. The ERROR-STATUS remains in effect (and checkable by your code) until the next statement with the NO-ERROR option.

When the ERROR condition occurs anywhere outside of a trigger block, Progress looks for the closest block with the error property and undoes and retries that block. As discussed earlier, if it is not meaningful to retry the block. Progress proceeds to the next iteration if it is a repeating block or else leaves the block.

Because this is the default, the following transaction header from the sample saveOrder procedure could simply be DO TRANSACTION: and have the same effect:

DO TRANSACTION ON ERROR UNDO, LEAVE: 

Error handling

If an error occurs in a database trigger block, Progress undoes the trigger block and returns ERROR.

If you use the NO-ERROR option on statements within a block, you are suppressing not only the visible error message but also the ERROR condition itself. Therefore, if you do this, it becomes your responsibility to check for errors and respond to them correctly. This might include issuing an UNDO statement of your own. The ON phrase in the header simply changes the default action for untrapped conditions.

RETURN statement and ON . . . RETURN phrase

In any RETURN statement, whether it returns an ERROR or not, you can return a text string to the calling procedure. This string is accessible in the caller through the RETURN-VALUE built-in function. Thus, a procedure can use a single RETURN statement to raise the ERROR condition in the caller, return a message, or both:

RETURN [ ERROR ] [ return-value-string ] . 

Likewise, the caller can check for the ERROR-STATUS:ERROR condition, or a RETURN-VALUE, or both, depending on the agreement between caller and callee as to how they communicate with one another. The RETURN-VALUE function retains its value even through multiple RETURN statements. Thus, while it is not required, it is advisable always to have a RETURN statement at the end of every procedure, if only to clear the RETURN-VALUE. A simple RETURN statement is the same as RETURN “”. If you want to pass the RETURN-VALUE up the call stack, you should do this explicitly using the statement RETURN RETURN-VALUE.

The same is true of the RETURN option as part of the ON phrase in a block header. It can return a return-value, raise ERROR, or both.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095